/******************************************************************************/
package jamie.org.sil.www.UsxVernacularCreator;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.*;
import java.awt.Font;
import java.io.*;
import javax.swing.*;
import javax.swing.table.*;


/******************************************************************************/
/**
    GUI support.

    Note that there appears to be a bug in the table handling, which means that
    it doesn't respond consistently to changes in font size.  For this reason,
    we have to store the required font size below, and then use it in each of
    the renderers to return a font of the appropriate size.
*/

public class GuiSupport
{
  /****************************************************************************/
  /****************************************************************************/
  /**                                                                        **/
  /**                               Public                                   **/
  /**                                                                        **/
  /****************************************************************************/
  /****************************************************************************/

  /****************************************************************************/
  public static XmlFile getXmlFile () { return m_XmlFile; }
  public static void setXmlFile (XmlFile xmlFile) { m_XmlFile = xmlFile; }
  public static float getTableFontSize () { return m_TableFontSize; }
  public static void setTableFontSize (float v) { m_TableFontSize = v; }


  /****************************************************************************/
  /** Returns an instance.  There's a separate instance for each output tab.
   *
   * @param tabNo
   * @return Instance.
  */

  public static GuiSupport instance (int tabNo)
  {
    if (null == m_Instance[tabNo]) m_Instance[tabNo] = new GuiSupport(tabNo);
    return m_Instance[tabNo];
  }


  /****************************************************************************/
  /** Shows a Yes / No / Cancel dialogue box.

      @return 0 => Yes, 1 => No, 2 => Cancel.
  */

  public static int confirmSave ()
  {
    return JOptionPane.showConfirmDialog(null,
                                         "Save changes?",
                                         "Save changes?",
                                         JOptionPane.YES_NO_CANCEL_OPTION);
  }


  /****************************************************************************/
  /** Shows a Yes / No dialogue box.

     @param prompt

     @return 0 => Yes, 1 => No.
  */

  public static int getYesNo (String prompt)
  {
    return JOptionPane.showConfirmDialog(null,
                                         prompt,
                                         "Confirm",
                                         JOptionPane.YES_NO_OPTION);
  }


  /****************************************************************************/
  /** Makes a check-box based editor.

      @return Editor
  */

  public static TableCellEditor makeCheckBoxBasedEditor ()
  {
    return new DefaultCellEditor(new JCheckBox());
  }

  
  /****************************************************************************/
  /** Makes a boolean renderer for text entry.
   *
   * @param optionality
   * @return Renderer
   */

  public DefaultTableCellRenderer makeBooleanRenderer (ConfigTag.Optionality optionality)
  {
    return new BooleanRenderer(optionality);
  }


  /****************************************************************************/
  /** Makes a combo-box based editor.

      @param options "|"-separated list of options.

      @return Editor
  */

  public static TableCellEditor makeComboBasedEditor (String options)
  {
    JComboBox comboBox = new JComboBox();
    String[] opts = options.split("\\|");
    for (int i = 0; i < opts.length; ++i) comboBox.addItem(opts[i]);
    return new DefaultCellEditor(comboBox);
  }


  /****************************************************************************/
  /** Makes a file-chooser-based editor.

      @param prompt Used in the title of the file chooser.

      @param dflt Default value

      @return Editor
  */

  public TableCellEditor makeFileEditor (String prompt, String dflt)
  {
    return new FileEditor(null, prompt, false, dflt);
  }


  /****************************************************************************/
  /** Makes a folder-chooser-based editor.

      @param prompt Used in the title of the file chooser.

      @param dflt Default value

      @return Editor
  */

  public TableCellEditor makeFolderEditor (String prompt, String dflt)
  {
    return new FileEditor(null, prompt, true, dflt);
  }


  /****************************************************************************/
  /** Makes a header renderer.
   *
   * @return Renderer
   */

  public TableCellRenderer makeHeaderRenderer  () { return m_HeaderRenderer;  }


  /****************************************************************************/
  /** Makes a prompt renderer.
   *
   * @param optionality
   * @return Renderer
   */

  public TableCellRenderer makePromptRenderer (ConfigTag.Optionality optionality)
  {
    return new PromptRenderer(optionality);
  }


  /****************************************************************************/
  /** Makes a text-field based editor.

      @return Editor
  */

  public static DefaultCellEditor makeTextFieldBasedEditor ()
  {
    return new DefaultCellEditor(new JTextField());
  }


  /****************************************************************************/
  /** Makes a text renderer for text entry.
   *
   * @param optionality
   * @return Renderer
   */

  public DefaultTableCellRenderer makeTextRenderer (ConfigTag.Optionality optionality)
  {
    return new TextRenderer(optionality);
  }


  /****************************************************************************/
  /** Shows an error message.

      @param msg Message.
  */

  public static void reportError (String msg)
  {
    String[] options = { "OK" };
    JOptionPane.showOptionDialog(null,
                                 msg,
                                 "Error",
                                 JOptionPane.DEFAULT_OPTION,
                                 JOptionPane.ERROR_MESSAGE,
                                 null,
                                 options,
                                 options[0]);
  }


  /****************************************************************************/
  /** Shows an error message.

      @param msg Message.
  */

  public static void reportInformation (String msg)
  {
    String[] options = { "OK" };
    JOptionPane.showOptionDialog(null,
                                 msg,
                                 "Information",
                                 JOptionPane.DEFAULT_OPTION,
                                 JOptionPane.ERROR_MESSAGE,
                                 null,
                                 options,
                                 options[0]);
  }


  /****************************************************************************/
  /** Shows a success message.

      @param msg Message.
  */

  public static void reportSuccess (String msg)
  {
    String[] options = { "OK" };
    JOptionPane.showOptionDialog(null,
                                 msg,
                                 "Information",
                                 JOptionPane.DEFAULT_OPTION,
                                 JOptionPane.INFORMATION_MESSAGE,
                                 null,
                                 options,
                                 options[0]);
  }


  /****************************************************************************/
  /** Runs a file-chooser.  This one is used when obtaining data for the
      various prompts.  It's convenient to keep this separate from the
      file-chooser used to obtain details of files which the GUI is reading
      and writing, since the two sets probably lead rather separate existences.

      @param comp Component relative to which to display the chooser.

      @param title

      @param saveOrOpen "save" or "open"

      @param directoriesOnly If true, deals only with directories, not with files.

      @param dflt If not empty, gives the default directory

      @return File name or empty string if cancelled.
  */

  public String showFileChooserDataEntry (Component comp,
                                          String title,
                                          String saveOrOpen,
                                          boolean directoriesOnly,
                                          String dflt)
  {
    m_FileChooserDataEntry.setDialogTitle(title);
    if (0 != dflt.length()) m_FileChooserDataEntry.setCurrentDirectory(new File(dflt));

    if (directoriesOnly)
    {
      m_FileChooserDataEntry.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
      m_FileChooserDataEntry.setFileFilter(null);
    }
    else
    {
      m_FileChooserDataEntry.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
      m_FileChooserDataEntry.setFileFilter(m_XmlFileFilter);
    }

    int returnVal = 0;
    if ("save".equalsIgnoreCase(saveOrOpen))
      returnVal = m_FileChooserDataEntry.showSaveDialog(comp);
    else
      returnVal = m_FileChooserDataEntry.showOpenDialog(comp);

    if (returnVal == JFileChooser.APPROVE_OPTION)
    {
      File file = m_FileChooserDataEntry.getSelectedFile();
      return file.getAbsolutePath();
    }

    return "";
  }



  



  /****************************************************************************/
  /****************************************************************************/
  /**                                                                        **/
  /**                              Private                                   **/
  /**                                                                        **/
  /****************************************************************************/
  /****************************************************************************/

  /****************************************************************************/
  private int m_TabNo = -1;
  private static XmlFile m_XmlFile = null;
  private static final JFileChooser m_FileChooser = new JFileChooser();
  private static final JFileChooser m_FileChooserDataEntry = new JFileChooser();
  private static float m_TableFontSize = 0;
  private static final XmlFileFilter m_XmlFileFilter = new XmlFileFilter();
  private static GuiSupport[] m_Instance = new GuiSupport[20];

  
  /****************************************************************************/
  GuiSupport (int paneNo) { m_TabNo = paneNo; }


  /****************************************************************************/
  public class BooleanRenderer extends DefaultTableCellRenderer
  {
    private ConfigTag.Optionality m_Optionality;

    BooleanRenderer (ConfigTag.Optionality optionality) { m_Optionality = optionality; }

    @Override public Component getTableCellRendererComponent (JTable table,
                                                              Object value,
                                                              boolean isSelected,
                                                              boolean hasFocus,
                                                              int row, int column)
    {
      boolean marked = (Boolean) value;
      JCheckBox rendererComponent = new JCheckBox();
      if (marked) rendererComponent.setSelected(true);
      setToolTipText(XmlDetails.instance().getTag(m_TabNo, row).getTooltip());
      setFont(getFont().deriveFont(getTableFontSize()));
      return rendererComponent;
    }
  }


  /****************************************************************************/
  final HeaderRenderer m_HeaderRenderer = new HeaderRenderer();
  public class HeaderRenderer extends DefaultTableCellRenderer
  {
    @Override public Component getTableCellRendererComponent (JTable table,
                                                              Object color,
                                                              boolean isSelected,
                                                              boolean hasFocus,
                                                              int row, int column)
    {
      if (0 == column)
        setValue(XmlDetails.instance().getTag(m_TabNo, row).getPrompt());
      else
        setValue("");

      Font f = getFont();
      f = f.deriveFont(f.getStyle() | Font.BOLD);
      setFont(f.deriveFont(getTableFontSize()));
      return this;
    }
  }


  /****************************************************************************/
  public class PromptRenderer extends DefaultTableCellRenderer
  {
    private ConfigTag.Optionality m_Optionality;

    PromptRenderer (ConfigTag.Optionality optionality) { m_Optionality = optionality; }

    @Override public Component getTableCellRendererComponent (JTable table,
                                                              Object color,
                                                              boolean isSelected,
                                                              boolean hasFocus,
                                                              int row, int column)
    {
      setValue(XmlDetails.instance().getTag(m_TabNo, row).getPrompt());
      setFont(getFont().deriveFont(getTableFontSize()));
      setHorizontalAlignment(JLabel.RIGHT);
      if (ConfigTag.Optionality.C_Mandatory == m_Optionality) setForeground(Color.red);
      return this;
    }
  }


  /****************************************************************************/
  public class TextRenderer extends DefaultTableCellRenderer
  {
    private ConfigTag.Optionality m_Optionality;

    TextRenderer (ConfigTag.Optionality optionality) { m_Optionality = optionality; }

    @Override public Component getTableCellRendererComponent (JTable table,
                                                              Object color,
                                                              boolean isSelected,
                                                              boolean hasFocus,
                                                              int row, int column)
    {
      ConfigTag.Generic tag = XmlDetails.instance().getTag(m_TabNo, row);
      setValue(tag.getValue());
      setToolTipText(tag.getTooltip());
      setBackground(Color.LIGHT_GRAY);
      setFont(getFont().deriveFont(getTableFontSize()));
      return this;
    }
  }


  /****************************************************************************/
  /* This code comes from the web, and arranges to put into a cell a button
     which invokes a file chooser.  Needs work, I think -- I don't like it as
     it stands.  In particular, it would be nice to be able to enter text
     without having to bring up the chooser; and I don't think the appearance
     of the button is terribly clear.
  */
  
  public class FileEditor extends AbstractCellEditor implements TableCellEditor, ActionListener
  {
    /**************************************************************************/
    protected static final String C_EDIT = "edit";
    private JButton m_Button;
    private String m_Dflt = "";
    private boolean m_DirectoriesOnly = false;
    private Component m_Component;
    private String m_FileName;
    private String m_Prompt;


    /**************************************************************************/
    public FileEditor(Component comp, String prompt, boolean directoriesOnly, String dflt)
    {
      m_Component = comp;
      m_Button = new JButton();
      m_Button.setActionCommand(C_EDIT);
      m_Button.addActionListener(this); // Possibly leak, but I'm not really worried.
      m_Button.setBorderPainted(false);
      m_Prompt = prompt;
      m_DirectoriesOnly = directoriesOnly;
      m_Dflt = dflt;
    }

    
    /**************************************************************************/
    /* Handles events from the editor button and from the dialog's OK button. */

    @Override public void actionPerformed (ActionEvent e)
    {
      if (C_EDIT.equals(e.getActionCommand()))
      {
        m_Button.setText(m_FileName);
        String fileName = showFileChooserDataEntry(m_Component, m_Prompt, "open", m_DirectoriesOnly, m_Dflt);
        if (0 != fileName.length())
        {
          m_FileName = fileName;
          fireEditingStopped(); //Make the renderer reappear.
        }
      }
    }


    /**************************************************************************/
    /* Implement the one CellEditor method that AbstractCellEditor doesn't
       handle. */

    @Override public Object getCellEditorValue() { return m_FileName; }


    /**************************************************************************/
    /* Implement the one method defined by TableCellEditor. */

    @Override public Component getTableCellEditorComponent (JTable table,
                                                            Object value,
                                                            boolean isSelected,
                                                            int row,
                                                            int column)
    {
      m_FileName = (String) value;
      return m_Button;
    }
  }
}


